#include "stdafx.h"
#include "ThreadPoolImpl.h"

namespace syncLib
{

    ThreadPoolImpl::ThreadPoolImpl(size_t maxThreads)
        : mutex_(FALSE), maxThreadCount_(maxThreads)
    {

        hNotEmptyEvent_.Attach(CreateEvent(0, FALSE, FALSE, NULL));
        if(!hNotEmptyEvent_)
        {
            throw std::runtime_error("Error ThreadPool initialization");
        }

        hStopEvent_.Attach(CreateEvent(0, TRUE, FALSE, NULL));
        if(!hStopEvent_)
        {
            throw std::runtime_error("Error ThreadPool initialization");
        }

    }


    ThreadPoolImpl::~ThreadPoolImpl(void)
    {
        WaitForDone(-1);
    }

    bool ThreadPoolImpl::IsEmptyTaks() const
    {
        return tasks_.empty();
    }

    bool ThreadPoolImpl::TryStartOnMoreThread()
    {
        if (ActiveThreadCount() <= MaxThreadCount())
        {
            StartThread();
            return true;
        }

        return false;
    }

    void ThreadPoolImpl::StartThread()
    {

        workers_.push_back(boost::thread(&ThreadPoolImpl::ThreadPoolFunction, this));

    }

    void ThreadPoolImpl::ScheduleTask(ITask* task)
    {
        if (!task)
        {
            throw std::runtime_error("Task cannot be null");
        }

        AutoMutex locker(mutex_);

        tasks_.push(task);

        SetEvent(hNotEmptyEvent_);

        TryStartOnMoreThread();
    }

    size_t ThreadPoolImpl::ActiveThreadCount() const
    {
        return workers_.size();
    }

    size_t ThreadPoolImpl::MaxThreadCount() const
    {
        return maxThreadCount_;
    }

    bool ThreadPoolImpl::WaitForDone(int /*msecs*/)
    {

        SetEvent(hStopEvent_);

        for (size_t i = 0; i < workers_.size(); ++i)
        {
            workers_[i].join();
        }

        return true;
    }

    void ThreadPoolImpl::ThreadPoolFunction()
    {
        for(;;)
        {
            mutex_.Lock();
            bool bEmptyPool = tasks_.empty();
            mutex_.UnLock();

            if(bEmptyPool)
            {
                HANDLE hWaitEvents[2] = { hStopEvent_, hNotEmptyEvent_ };

                switch(WaitForMultipleObjects(2, hWaitEvents, FALSE, INFINITE))
                {
                    // stop event was signaled
                case WAIT_OBJECT_0:
                case WAIT_ABANDONED:
                    {
                        std::cout<<"        Stop signal was received"<<std::endl;
                        return;
                    }
                    // task pool isn't empty, pop and execute it
                case WAIT_OBJECT_0 + 1:
                    {

                        std::cout<<"Ready signal was received"<<std::endl;

                        PopAndExecuteTask();


                    }


                    continue;

                }
                break;
            }

            PopAndExecuteTask();

        }
    }

    void ThreadPoolImpl::PopAndExecuteTask()
    {
        ITask* currentTask = NULL;

        mutex_.Lock();

        if(!tasks_.empty())
        {
            currentTask = tasks_.front();
            tasks_.pop();

        }

        mutex_.UnLock();

        if (currentTask)
        {
            currentTask->Run();
            delete currentTask;
        }
    }

};